﻿using AutoMapper;
using HamcoWcs.Contract.ContractCommon;
using HamcoWcs.DAL;
using HamcoWcs.Entitys.EdgeInfos;
using HamcoWcs.Models.Common.Enums;
using HamcoWcs.Models.EdgeInfos;
using HamcoWcs.Models.NodeInfos;
using HamcoWcs.Models.PathInfos;
using HamcoWcs.Models.WcsLocations;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;

namespace HamcoWcs.Service.Common
{
    [AutoInject(typeof(IPathService), InjectType.Single)]
    public class PathService : IPathService
    {
        private readonly List<NodeInfoDto> ErrorNodes = new List<NodeInfoDto>();
        private readonly IServiceScopeFactory _scopeFactory;
        private readonly IMapper _mapper;
        private readonly Dictionary<string, WcsLocationDto> _wcsLocationDtos = new Dictionary<string, WcsLocationDto>();


        private Dictionary<string, EdgeInfoDto> _edgeDtos = new Dictionary<string, EdgeInfoDto>();
        private Dictionary<string, NodeInfoDto> _nodeDtos = new Dictionary<string, NodeInfoDto>();
        private Dictionary<string, PathInfoDto> _pathDtos = new Dictionary<string, PathInfoDto>();

        private bool IsVcv100;


        private EdgeInfoDto CreateEdge(NodeInfoDto f, NodeInfoDto t)
        {
            var edge = new EdgeInfoDto()
            {
                EdgeName = $"{f.NodeName}-{t.NodeName}",
                FromNode = f.NodeName,
                ToNode = t.NodeName,
                FromNodeDto = f,
                ToNodeDto = t,
                ExcutePlc = "Pcon01",
                EdgeType = EnumSubTaskType.PconTask
            };

            if (f.DeviceType == EnumNodeType.con && t.DeviceType == EnumNodeType.srm)
            {
                edge.EdgeType = EnumSubTaskType.SrmInTask;
            }
            else if (f.DeviceType == EnumNodeType.srm && t.DeviceType == EnumNodeType.con)
            {
                edge.EdgeType = EnumSubTaskType.SrmOutTask;
            }
            else if (t.NodeName == f.NodeName)
            {
                edge.EdgeType = EnumSubTaskType.SrmMoveTask;
            }
            else if ((t.NodeName.Contains("CJRCV") && f.NodeName.Contains("CJVCV"))
                    || (t.NodeName.Contains("CJVCV") && f.NodeName.Contains("CJRCV"))
                    || (t.NodeName.Contains("CJVCV") && f.NodeName.Contains("CJVCV"))
                    )
            {
                edge.EdgeType = EnumSubTaskType.SrmStraightOutTask;
            }

            if (edge.EdgeType != EnumSubTaskType.PconTask)
            {
                edge.ExcutePlc = "Srm";
            }

            return edge;
        }

        private Dictionary<string, EdgeInfoDto> CreateEdgeDtos(List<NodeInfoDto> nodeDtos, WcsDbContext wcsDbContext)
        {
            var edgeDic = new Dictionary<string, EdgeInfoDto>();

            foreach (var node in nodeDtos)
            {
                if (string.IsNullOrEmpty(node.NextNode)) continue;
                var nextnodes = node.NextNode.Split(';');

                foreach (var nextNode in nextnodes)
                {
                    var edgeName = $"{node.NodeName}-{nextNode}";
                    Console.WriteLine(edgeName);
                    if (edgeDic.ContainsKey(edgeName)) continue;

                    var fromNode = _nodeDtos[node.NodeName];
                    var toNode = _nodeDtos[nextNode];

                    var edgeInfo = CreateEdge(fromNode, toNode);
                    edgeDic.Add(edgeName, edgeInfo);
                }
            }

            var edges = _mapper.Map<List<EdgeInfo>>(edgeDic.Values);
            wcsDbContext.EdgeInfos.AddRange(edges);
            wcsDbContext.SaveChanges();

            return edgeDic;
        }

        public PathService(IServiceScopeFactory scopeFactory, IMapper mapper)
        {
            _mapper = mapper;
            _scopeFactory = scopeFactory;

            Synchronous();
        }

        private void CalcPathEdge(PathInfoDto path)
        {
            var paths = path.UsedPath.Split('-');

            path.EdgeDtos.Clear();
            if (paths.Length < 2)
            {
                throw new ArgumentOutOfRangeException(nameof(paths));
            }
            else if (paths.Length == 2)
            {
                path.EdgeDtos.Add(_edgeDtos[path.UsedPath]);
            }
            else
            {
                for (int i = 0; i < paths.Length - 1; i++)
                {
                    var edgeName = $"{paths[i]}-{paths[i + 1]}";
                    path.EdgeDtos.Add(_edgeDtos[edgeName]);
                }
            }
        }

        private void Print(string msg, ConsoleColor color = ConsoleColor.White)
        {
            Console.ForegroundColor = color;
            Console.WriteLine(msg);
            Console.ForegroundColor = ConsoleColor.White;
        }



        public string CountPath()
        {
            throw new NotImplementedException();
        }
        private void FindNextPath(List<EdgeInfoDto> edges, NodeInfoDto lastNode, NodeInfoDto nowNode)
        {
            AddEdge(lastNode, nowNode);
            var edge = _edgeDtos[lastNode.NodeName + '-' + nowNode.NodeName];
            if (edges.Contains(edge))
                return;
            // todo  edges.Last().ExcutePlc == edge.ExcutePlc  等于'-'
            if (edges.Count != 0 && edges.Last().ExcutePlc == edge.ExcutePlc)
                return;
            edges.Add(edge);
            //AddPath(edges);
            if (string.IsNullOrEmpty(nowNode.NextNode))
                return;
            var nextNodeStrs = nowNode.NextNode.Split(';');
            foreach (var nextNodeStr in nextNodeStrs)
            {
                //下一个节点不存在，结束递归
                if (nextNodeStr == "-")
                    continue;
                if (!_nodeDtos.ContainsKey(nextNodeStr))
                    continue;
                //var newEdgeList = new List<EdgeInfoDto>();
                //newEdgeList.AddRange(edges);
                //FindNextPath(newEdgeList, nowNode, _nodeDtos[nextNodeStr]);
                FindNextPath(edges, nowNode, _nodeDtos[nextNodeStr]);
            }
        }
        private void AddEdge(NodeInfoDto fromNode, NodeInfoDto toNode)
        {
            //var edgeName = fromNode.NodeName + '-' + toNode.NodeName;
            //if (_edgeDtos.ContainsKey(edgeName))
            //    return;
            //string excutePlc = "";
            //if (fromNode.DeviceType == "con" && toNode.DeviceType == "con")
            //    excutePlc = fromNode.PlcName;
            //else if (fromNode.DeviceType == "con")
            //    excutePlc = toNode.PlcName;
            //else
            //    excutePlc = fromNode.PlcName;
            //EdgeInfoDto edgeInfo = new EdgeInfoDto()
            //{
            //    EdgeName = edgeName,
            //    EdgeType = 0,
            //    ExcutePlc = excutePlc,
            //    FromNode = fromNode.NodeName,
            //    ToNode = toNode.NodeName
            //};
            //edgeInfo.FromNodeDto = _nodeDtos[edgeInfo.FromNode];
            //edgeInfo.ToNodeDto = _nodeDtos[edgeInfo.ToNode];
            //_edgeDtos.TryAdd(edgeInfo.EdgeName, edgeInfo);
        }

        //private void AddPath(List<EdgeInfoDto> edges)
        //{
        //    var srcNode = _nodeDtos[edges.First().FromNode];
        //    var nowNode = _nodeDtos[edges.Last().ToNode];
        //    //无wmsname不需要计算路径,继续递归
        //    if (string.IsNullOrEmpty(srcNode.WmsNodeName) || string.IsNullOrEmpty(nowNode.WmsNodeName))
        //        return;
        //    var pathName = srcNode.WmsNodeName + '-' + nowNode.WmsNodeName;
        //    if (_pathDtos.ContainsKey(pathName))
        //        return;
        //    StringBuilder sb = new StringBuilder();
        //    foreach (var edge in edges)
        //    {
        //        sb.Append(edge.FromNode);
        //        sb.Append("-");
        //    }
        //    sb.Append(nowNode.NodeName);
        //    PathInfoDto pathInfoDto = new PathInfoDto()
        //    {
        //        PathName = pathName,
        //        FromWmsNode = srcNode.WmsNodeName,
        //        ToWmsNode = nowNode.WmsNodeName,
        //        PathValue = sb.ToString(),
        //        EdgeDtos = edges
        //    };
        //    //pathInfoDto.EdgeDtos.AddRange(edges);
        //    _pathDtos.TryAdd(pathInfoDto.PathName, pathInfoDto);
        //    return;
        //}

        public PathInfoDto GetPath(string from, string to)
        {
            var key = from + '-' + to;
            return _pathDtos.ContainsKey(key) ? _pathDtos[from + '-' + to] : null;
        }

        public Dictionary<string, PathInfoDto> GetAllPath()
        {
            return _pathDtos;
        }
        public NodeInfoDto GetWmsNode(string nodeName)
        {
            //throw new NotImplementedException();
            return _nodeDtos.Values.FirstOrDefault(x => x.NodeName == nodeName);
        }
        public WcsLocationDto GetLocation(string locName)
        {
            return _wcsLocationDtos.ContainsKey(locName) ? _wcsLocationDtos[locName] : null;
        }
        public NodeInfoDto GetWcsNode(string nodeName)
        {
            return _nodeDtos.Values.FirstOrDefault(x => x.NodeName == nodeName);
        }

        public NodeInfoDto GetSrmNode(string nodeName)
        {
            return _nodeDtos.Values.FirstOrDefault(x => x.NodeName == nodeName);
        }



        public void AddErrorNode(string nodeName)
        {
            var node = _nodeDtos.Values.FirstOrDefault(x => x.NodeName == nodeName);
            node.ErrorTime = DateTime.Now;
            var errnode = ErrorNodes.FirstOrDefault(x => x.NodeName == nodeName);
            if (errnode is null)
            {
                ErrorNodes.Add(node);
            }
        }

        public void RemoveErrorNode(string nodeName)
        {
            var errnode = ErrorNodes.FirstOrDefault(x => x.NodeName == nodeName);
            if (errnode != null)
            {
                ErrorNodes.Remove(errnode);
            }
        }

        public void CalcRoute()
        {
            var now = DateTime.Now;
            // 超过20分钟未处理
            if (ErrorNodes.Any(x => x.ErrorTime.AddMinutes(20) < now))
            {
                // todo 提升机或提升机出来的CV当即切换路径
            }

            throw new NotImplementedException();
        }

        public void RerouteTask()
        {
            throw new NotImplementedException();
        }

        public void Synchronous()
        {
            using (var scope = _scopeFactory.CreateScope())
            {
                var _wcsDbContext = scope.ServiceProvider.GetRequiredService<WcsDbContext>();

                var nodeDtos = _mapper.Map<List<NodeInfoDto>>(_wcsDbContext.NodeInfos.ToList());
                _nodeDtos = nodeDtos.ToDictionary(k => k.NodeName, v => v);
                var edgeDtos = _mapper.Map<List<EdgeInfoDto>>(_wcsDbContext.EdgeInfos.ToList());
                var pathDtos = _mapper.Map<List<PathInfoDto>>(_wcsDbContext.PathInfos.ToList());


                foreach (var edgeDto in edgeDtos )
                {
                    edgeDto.FromNodeDto = _nodeDtos[edgeDto.FromNode];
                    edgeDto.ToNodeDto = _nodeDtos[edgeDto.ToNode];
                }

                _edgeDtos = edgeDtos.ToDictionary(k => k.EdgeName, v => v);

                if (_edgeDtos.Count <= 0) return;

                foreach (var path in pathDtos)
                {
                    try
                    {
                        CalcPathEdge(path);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(path.PathName);
                    }
                }

                _pathDtos = pathDtos.ToDictionary(k => k.PathName, v => v);
            }
        }

        public void ReRoutePathEdges()
        {
            foreach (var path in _pathDtos.Values)
            {
                try
                {
                    CalcPathEdge(path);

                }
                catch (Exception e)
                {
                    Console.WriteLine(path.PathName);
                }
            }
        }

        public void ReRoutePathEdges(string pathName)
        {
            var path = _pathDtos[pathName];
            try
            {
                CalcPathEdge(path);
            }
            catch (Exception e)
            {
                Console.WriteLine(path.PathName);
            }
        }

        public bool TogglePath()
        {
            IsVcv100 = !IsVcv100;
            return IsVcv100;
        }
    }
}
